/********************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2009.
-------------------------------------------------------------------------
File name:   AIDebugRenderer.h
$Id$
Description: Base class for AI debug renderer with decoding Render method

-------------------------------------------------------------------------
History:
- 2006-9-19   : Created (AIDebugDrawHelpers.h) by Mikko Mononen
- 2009-2-11   : class CAIVisualDebugRenderer by Evgeny Adamenkov

*********************************************************************/

#ifndef _AI_VISUAL_DEBUG_RENDERER_H_
#define _AI_VISUAL_DEBUG_RENDERER_H_

#if _MSC_VER > 1000
#pragma once
#endif


#include "AIAbstractDebugRenderer.h"

#include <vector>


class CAIVisualDebugRenderer : public CAIAbstractDebugRenderer
{
	IRenderer* m_pRenderer;
	IRenderAuxGeom* m_pRenderAuxGeom;

public:
	CAIVisualDebugRenderer(IRenderer* pRenderer);


	//////////////////////////////////////////////////////////////////////////
	//
	//	IAIDebugRenderer interface methods

	virtual Vec3  GetCameraPos() { return gEnv->pSystem->GetViewCamera().GetPosition(); }
	virtual float GetCameraFOV() { return m_pRenderer->GetCamera().GetFov(); }

	virtual int GetWidth()	{ return m_pRenderer->GetWidth(); }
	virtual int GetHeight()	{ return m_pRenderer->GetHeight(); }

	virtual void DrawAABB(const AABB& aabb, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle);
	virtual void DrawAABB(const AABB& aabb, const Matrix34& matWorld, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle);
	virtual void DrawArrow(const Vec3& vPos, const Vec3& vLength, float fWidth, const ColorB& color);
	virtual void DrawCapsuleOutline(const Vec3& vPos0, const Vec3& vPos1, float fRadius, const ColorB& color);
	virtual void DrawCircleOutline(const Vec3& vPos, float fRadius, const ColorB& color);
	virtual void DrawCircles(const Vec3& vPos, float fMinRadius, float fMaxRadius, int numRings, const ColorB& insideColor, const ColorB& outsideColor);
	virtual void DrawCone(const Vec3& vPos, const Vec3& vDir, float fRadius, float fHeight, const ColorB& color, bool bDrawShaded = true);
	virtual void DrawCylinder(const Vec3& vPos, const Vec3& vDir, float fRadius, float fHeight, const ColorB& color, bool bDrawShaded = true);
	virtual void DrawEllipseOutline(const Vec3& vPos, float fRadiusX, float fRadiusY, float fOrientation, const ColorB& color);
	virtual void Draw2dLabel(int nCol, int nRow, const char* szText, const ColorB& color);
	virtual void Draw2dLabel(float fX, float fY, float fFontSize, const ColorB& color, bool bCenter, const char* format, ...) PRINTF_PARAMS(7, 8);
	virtual void Draw3dLabel(const Vec3& vPos, float fFontSize, const char* format, ...) PRINTF_PARAMS(4, 5);
	virtual void Draw3dLabelEx(const Vec3& vPos, float fFontSize, const ColorB& color, bool bFixedSize, bool bCenter, const char* format, ...) PRINTF_PARAMS(7, 8);
	virtual void Draw2dImage(float fX, float fY, float fWidth, float fHeight, int nTextureID, float fS0 = 0.f, float fT0 = 0.f, float fS1 = 1.f, float fT1 = 1.f, float fAngle = 0.f, float fR = 1.f, float fG = 1.f, float fB = 1.f, float fA = 1.f, float fZ = 1.f);
	virtual void DrawLine(const Vec3& v0, const ColorB& colorV0, const Vec3& v1, const ColorB& colorV1, float fThickness = 1.f);
	virtual void DrawOBB(const OBB& obb, const Vec3& vPos, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle);
	virtual void DrawOBB(const OBB& obb, const Matrix34& matWorld, bool bSolid, const ColorB& color, EBoundingBoxDrawStyle bbDrawStyle);
	virtual void DrawPolyline(const Vec3* va, uint32 nPoints, bool bClosed, const ColorB& color, float fThickness = 1.f);
	virtual void DrawPolyline(const Vec3* va, uint32 nPoints, bool bClosed, const ColorB* colorArray, float fThickness = 1.f);
	virtual void DrawRangeArc(const Vec3& vPos, const Vec3& vDir, float fAngle, float fRadius, float fWidth, const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline);
	virtual void DrawRangeBox(const Vec3& vPos, const Vec3& vDir, float fSizeX, float fSizeY, float fWidth, const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline);
	virtual void DrawRangeCircle(const Vec3& vPos, float fRadius, float fWidth, const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline);
	virtual void DrawRangePolygon(const Vec3* va, uint32 nPoints, float fWidth, const ColorB& colorFill, const ColorB& colorOutline, bool bDrawOutline);
	virtual void DrawSphere(const Vec3& vPos, float fRadius, const ColorB& color, bool bDrawShaded = true);
	virtual void DrawTriangle(const Vec3& v0, const ColorB& colorV0, const Vec3& v1, const ColorB& colorV1, const Vec3& v2, const ColorB& colorV2);
	virtual void DrawTriangles(const Vec3* va, uint32 numPoints, const ColorB& color);
	virtual void DrawWireFOVCone(const Vec3& vPos, const Vec3& vDir, float fRadius, float fFOV, const ColorB& color);
	virtual void DrawWireSphere(const Vec3& vPos, float fRadius, const ColorB& color);

	virtual ITexture* LoadTexture(const char* szNameOfTexture, uint32 nFlags);

	// [9/16/2010 evgeny] ProjectToScreen is not guaranteed to work if used outside Renderer
	virtual bool ProjectToScreen(float fInX, float fInY, float fInZ, float* pfOutX, float* pfOutY, float* pfOutZ);

	virtual void TextToScreen(float fX, float fY, const char* format, ...) PRINTF_PARAMS(4, 5);
	virtual void TextToScreenColor(int nX, int nY, float fRed, float fGreen, float fBlue, float fAlpha, const char* format, ...) PRINTF_PARAMS(8, 9);

	virtual void SetMaterialColor(float fRed, float fGreen, float fBlue, float fAlpha);

	virtual void SetFlags(SAuxGeomRenderFlags flags);
	virtual int GetFrameID();
	virtual void DebugDraw();

	
	//////////////////////////////////////////////////////////////////////////
	//
	//	Network AI Debug Draw receiver
	
	void ConnectReceiver   (INotificationNetwork* pNotificationNetwork);
	void DisconnectReceiver(INotificationNetwork* pNotificationNetwork);
	
	
private:

	//////////////////////////////////////////////////////////////////////////
	//
	//	Deserialization helper
	
	class CDrawCommandsInputStream : public INotificationNetworkListener
	{
		CryMutex m_mutexReceiveOrRender;
	
		std::vector<uint8> m_buffer;
		size_t index;
		bool m_bNeedsEndianSwap;
		
	public:
		CDrawCommandsInputStream() : index(0), m_bNeedsEndianSwap(true), m_deliveryID(0), m_deliveryRate(0)
		{
		}
	
		// The INotificationNetworkListener interface method
		virtual void OnNotificationNetworkReceive(const void* pBuffer, size_t size)
		{
			assert(size > 0);
			
			Lock();
			static float previousTime = gEnv->pTimer->GetCurrTime();
			float currentTime = gEnv->pTimer->GetCurrTime();
			float deltaTime = currentTime - previousTime;
			m_deliveryRate = (deltaTime > 0.0001f) ? (static_cast<int>(size / deltaTime)) : 0;
			previousTime = currentTime;
			
			++m_deliveryID;
			m_buffer.resize(size);
			memcpy(&m_buffer[0], pBuffer, size);
			Rewind();
			m_bNeedsEndianSwap = true;
			Unlock();
		}
		
		void Clear() { m_buffer.clear(); index = 0; }
		
		bool HasMoreCommands() { return index < m_buffer.size(); }
		
		void Rewind() { index = 0; }
		
		template <typename T>
		T Read()
		{
			assert(HasMoreCommands());
			typedef typename SerialTypeOf<T>::type SerialType;
			SerialType* pValue = reinterpret_cast<SerialType*>(&m_buffer[index]);

#ifdef NEED_ENDIAN_SWAP
			if (m_bNeedsEndianSwap)
			{
				SwapEndian(*pValue);
			}
#endif

			SerialType value = *pValue;
			index += sizeof(SerialType);
			assert(index <= m_buffer.size());
			return static_cast<T>(value);
		}
	
		// List here all types that you would like to get by value, rather than by reference
		
		//operator AABB()                  { return Read<AABB>(); }
		operator bool()                  { return Read<bool>(); }
		//operator ColorB()                { return Read<ColorB>(); }
		operator float()                 { return Read<float>(); }
		operator EBoundingBoxDrawStyle() { return Read<EBoundingBoxDrawStyle>(); }
		operator EDrawCommandType()      { return Read<EDrawCommandType>(); }
		operator int32()                 { return Read<int32>(); }
		//operator Matrix34()              { return Read<Matrix34>(); }
		//operator OBB()                   { return Read<OBB>(); }
		operator uint32()                { return Read<uint32>(); }
		//operator Vec3()                  { return Read<Vec3>(); }


		template <typename T>
		const T* Read(size_t size)
		{
			assert(HasMoreCommands());
			typedef typename SerialTypeOf<T>::type SerialType;
			assert(sizeof(T) == sizeof(SerialType));
			T* address = reinterpret_cast<T*>(&m_buffer[index]);

#ifdef NEED_ENDIAN_SWAP
			if (m_bNeedsEndianSwap)
			{
				for (size_t i = 0; i < size; ++i)
				{
					SwapEndian(address[i]);
				}
			}
#endif

			index += size * sizeof(SerialType);
			assert(index <= m_buffer.size());
			return address;
		}

		template <typename T>
		operator const T&()
		{
			assert(HasMoreCommands());
			typedef typename SerialTypeOf<T>::type SerialType;
			assert(sizeof(T) == sizeof(SerialType));
			SerialType* address = reinterpret_cast<SerialType*>(&m_buffer[index]);

#ifdef NEED_ENDIAN_SWAP
			if (m_bNeedsEndianSwap)
			{
				SwapEndian(*address);
			}
#endif

			index += sizeof(SerialType);
			assert(index <= m_buffer.size());
			return static_cast<const T&>(*address);
		}

		operator const char*()
		{ 
			assert(HasMoreCommands());
			const char* str = reinterpret_cast<const char*>(&m_buffer[index]);
			index += strlen(str) + 1;
			assert(index <= m_buffer.size());
			return str;
		}
		
		void Lock()   { m_mutexReceiveOrRender.Lock(); }
		void Unlock() { m_mutexReceiveOrRender.Unlock(); }

		bool Empty() { return m_buffer.empty(); }

		void SetNeedsEndianSwap(bool bNeedsEndianSwap) { m_bNeedsEndianSwap = bNeedsEndianSwap; }

		// Exclusively for debugging
		size_t Size() { return m_buffer.size(); }
		size_t m_deliveryID;
		int    m_deliveryRate;

	};	// class CDrawCommandsInputStream


	CDrawCommandsInputStream m_inputStream;

	// ProjectToScreen
	Vec3 m_vPointToProject;
	Vec3 m_vProjectedPoint;
};


#endif	// #ifndef _AI_VISUAL_DEBUG_RENDERER_H_
